home *** CD-ROM | disk | FTP | other *** search
/ IRIX 6.2 Applications 1996 May / SGI IRIX 6.2 Applications 1996 May.iso / dist / impr_dev.idb / usr / impressario / src / drivers / phandler / phandler.c.z / phandler.c
C/C++ Source or Header  |  1996-05-06  |  37KB  |  1,086 lines

  1. /**************************************************************************
  2.  *
  3.  *                Copyright (c) 1992 Silicon Graphics, Inc.
  4.  *                      All Rights Reserved
  5.  *
  6.  *         THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SGI
  7.  *
  8.  * The copyright notice above does not evidence any actual of intended
  9.  * publication of such source code, and is an unpublished work by Silicon
  10.  * Graphics, Inc. This material contains CONFIDENTIAL INFORMATION that is
  11.  * the property of Silicon Graphics, Inc. Any use, duplication or
  12.  * disclosure not specifically authorized by Silicon Graphics is strictly
  13.  * prohibited.
  14.  *
  15.  * RESTRICTED RIGHTS LEGEND:
  16.  *
  17.  * Use, duplication or disclosure by the Government is subject to
  18.  * restrictions as set forth in subdivision (c)(1)(ii) of the Rights in
  19.  * Technical Data and Computer Software clause at DFARS 52.227-7013,
  20.  * and/or in similar or successor clauses in the FAR, DOD or NASA FAR
  21.  * Supplement. Unpublished - rights reserved under the Copyright Laws of
  22.  * the United States. Contractor is SILICON GRAPHICS, INC., 2011 N.
  23.  * Shoreline Blvd., Mountain View, CA 94039-7311
  24.  **************************************************************************
  25.  *
  26.  * File:
  27.  *   phandler.c:
  28.  *
  29.  * Description:
  30.  *    Checks the status of the built-in parallel port, while passing data 
  31.  *    through to the built-in parallel port, buffering an optional # of bytes,
  32.  *    and updating the printer status database as it goes.
  33.  *
  34.  *    Can also be called to update status of the pod 
  35.  *    file ~lp/pod/<printername.status> and then return without sending 
  36.  *    and data to parallel port.
  37.  *
  38.  * See Also:
  39.  *    Developers will want to be familiar with the SGI filter/driver spec
  40.  *    to understand exactly which options their drivers MUST implement,
  41.  *    how options are to be parsed, and which option letters are reserved.
  42.  *
  43.  *    Developers may be well advised to read the following man pages
  44.  *    for more information on the libraries and functions used in
  45.  *    this program:
  46.  *
  47.  *    libpod -- general information on POD database library
  48.  *    plp    -- information on the builtin parallel port interface
  49.  *
  50.  *    Specific libpod function calls are also documented, see their man pages.
  51.  *
  52.  **************************************************************************/
  53.  
  54. #ident "$Revision: 1.38 $"
  55.  
  56. #include "phandler.h"
  57. #include "impr_versions.h"
  58.  
  59.         /* Variables set by command line options */
  60.  
  61. static  short       exit_on_error = FALSE; /* Exit on first error or retry? */
  62. static  short       status_only = FALSE;   /* Exit after reporting status? */
  63. static  short       ignore_plp = FALSE;    /* Ignore plp port status? */
  64. static  short       print_warnings = TRUE; /* Report warnings in status? */
  65. static  short       reset_port = FALSE;    /* Reset plp before writing? */
  66. static  short       ignore_EOI = TRUE;     /* Ignore the EOI status bit? */
  67.  
  68. static  short       ignore_interrupts = FALSE;
  69.  
  70. static  short       DEBUG = 0;       /* What level debugging is turned on: */
  71.                                      /* 0-3==none,COARSE,MEDIUM,FINE DETAIL*/
  72. static  char       *printername;     /* printer name (required cmd line arg)  */
  73. static  char       *progname;        /* Name of this program when run, less path */
  74.  
  75.         /* I/O buffer and sizes thereof */
  76.  
  77. #define BUFDEFSIZE     1024*1024
  78. static  int        inbufsize = BUFDEFSIZE;    /* size of buffer to allocate   */
  79. static  char      *inbuf = NULL;              /* start of input data buffer   */
  80. static  int        inbytesleft;               /* number of bytes left to read */
  81.  
  82. static pid_t child_pid = -1;                  /* process if of forked child   */
  83.  
  84.         /* Named paths to essential files and output port */
  85.  
  86. static  FILE      *errfile = stderr;    /* log output to stderr or file */
  87. static  FILE      *infile = stdin;      /* input from stdin or file */
  88.  
  89. static short page_size_set = FALSE;
  90. static int user_page_size = DEFAULT_PAGE_SIZE;
  91. static int send_ctrl_d = FALSE;         /* If true, send ctrlD at EOJ */
  92.  
  93.         /* Printer POD information database structures */
  94.  
  95. static  PDInfoStruct    *pod;           /* printer object database struct*/
  96. static  time_t           podmodtime;    /* time of last POD modification */
  97. static  PDMessageStruct  errorMessages[PD_MESSAGE_MAX]; /* Error messages */
  98.  
  99.  
  100. /***********************************************************************/
  101.  
  102. void set_program_name(char* name)
  103.     /* REQUIRES: nothing
  104.      * MODIFIES: global var progname
  105.      * EFFECTS:  Sets global name of program (progname).
  106.      *           Strips off any included path and leaves real name.
  107.      */
  108. {
  109.     if ((progname = strrchr(name,'/')) != NULL) progname++;
  110.     else progname = name;
  111. }
  112.  
  113. /***********************************************************************/
  114.  
  115. void  hold_signals(void) {
  116.     /* REQUIRES: nothing
  117.      * MODIFIES: signal handling environment
  118.      * EFFECTS:  puts key signals on hold until release_signals is called.
  119.      *
  120.      * This function HOLDS the most common catchable interrupts.
  121.      * This is important in the UNIX environment because many
  122.      * software interrupts (signals) are possible.
  123.      */
  124.     (void)sighold(SIGINT);
  125.     (void)sighold(SIGTERM);
  126.     (void)sighold(SIGHUP);
  127.     (void)sighold(SIGQUIT);
  128. }
  129.  
  130. /***********************************************************************/
  131.  
  132. void  release_signals(void) {
  133.     /* REQUIRES: nothing
  134.      * MODIFIES: signal handling environment
  135.      * EFFECTS:  releases key signals from any hold condition
  136.      *
  137.      * This function RELEASES the most common catchable interrupts.
  138.      * This is important in the UNIX environment because many
  139.      * software interrupts (signals) are possible.
  140.      */
  141.     if (ignore_interrupts && (child_pid != (pid_t) 0) ) return;
  142.     (void)sigrelse(SIGINT);
  143.     (void)sigrelse(SIGTERM);
  144.     (void)sigrelse(SIGHUP);
  145.     (void)sigrelse(SIGQUIT);
  146. }
  147.  
  148. /***********************************************************************/
  149.  
  150. void cleanup_and_quit(int exitcode)
  151.     /* ASSUMES:  Program should exit upon handling this error.
  152.      * MODIFIES: out
  153.      * EFFECTS:  Cleans up printer and exits.
  154.      *           Used to handle interrupts
  155.      */
  156. {
  157. int ii;
  158.     /* Only called if a signal sent.  Normal exit is through main.
  159.      * exception is the child process which exits here when signaled
  160.      * by parent process to quit.
  161.      */
  162.  
  163.     hold_signals();
  164.  
  165.     if (child_pid == (pid_t) 0) {  /* We are child */
  166.         if (DEBUG) {
  167.            fprintf(stderr,"%s: cleanup_and_quit called in child.\n",progname);
  168.         }
  169.         exit(exitcode);
  170.     }
  171.  
  172.     if (DEBUG) {
  173.        fprintf(stderr,"%s: cleanup_and_quit called in parent.\n",progname);
  174.     }
  175.  
  176.     /* Drain any remaining input */
  177.  
  178.     if (inbuf) {
  179.          while (fgets(inbuf,inbufsize,infile))
  180.                   ;
  181.     }
  182.  
  183.     /* If the port status is failed we need to hang around until it
  184.      * is OK.  Imagine case were printer goes down and we changed the
  185.      * printer icon to a failed printer.  The user then uses cancel to
  186.      * cancel the job sending us this interrupt we are handling.  If we
  187.      * return the spooler will send out the next job (if there is one).
  188.      * Or, if there is no next job we leave the printer status as
  189.      * unavailable causing the icon to be the unavialable icon -- but when
  190.      * printer is re-connected the icon will not be updated (unless we 
  191.      * continue to monitor it here).
  192.      */
  193.  
  194.     (void) update_status(PD_STATUS_IDLE);
  195.  
  196.     if (!ignore_plp) {
  197.         while (pod->active_status->operational_status != PD_STATUS_IDLE) {
  198.             if (DEBUG) {
  199.                fprintf(stderr,"%s: Waiting for printer error to clear.\n",
  200.                        progname);
  201.             }
  202.             (void) sleep(pod->error_retry_wait);
  203.             (void) update_status(PD_STATUS_IDLE);
  204.         }
  205.  
  206.         if (inbuf) {
  207.             if (send_ctrl_d) {
  208.                 inbuf[0]='\004';
  209.                 inbuf[1]='\0';
  210.                 (void) fwrite(inbuf,1,1,stdout);
  211.             }
  212.         }
  213.     }
  214.  
  215.     exit(exitcode);
  216.  
  217. }
  218.  
  219. /***********************************************************************/
  220.  
  221. void  setup_signal_handler(void) {
  222.     /* REQUIRES: nothing
  223.      * MODIFIES: signal handling environment
  224.      * EFFECTS:
  225.      *
  226.      * Sets up signal handler to catch the killing signals
  227.      *  so that proper cleanup can be done
  228.      *
  229.      * This is important in the UNIX environment because many
  230.      * software interrupts are possible.  We catch the most common
  231.      * catchable interrupts.
  232.      *
  233.      * Cleanup_and_quit is a void(int ...) function which
  234.      * should clean up the printer and then exit the program.
  235.      */
  236.  
  237.     (void) signal(SIGINT, cleanup_and_quit);
  238.     (void) signal(SIGTERM, cleanup_and_quit);
  239.     (void) signal(SIGHUP, cleanup_and_quit);
  240.     (void) signal(SIGQUIT, cleanup_and_quit);
  241. }
  242.  
  243. /***********************************************************************/
  244.  
  245. void print_usage_message(void)
  246.     /* REQUIRES: nothing
  247.      * MODIFIES: errfile
  248.      * EFFECTS:  prints usage message
  249.      */
  250. {
  251.     fprintf(errfile, "\nUSAGE:\n       %s", progname);
  252.     fprintf(errfile, "\t-P Printer name. (REQUIRED)\n");
  253.     fprintf(errfile, "\t\t[-e]        Exit immediately on error.\n");
  254.     fprintf(errfile, "\t\t[-w]        Suppress warning reporting.\n");
  255.     fprintf(errfile, "\t\t[-D]        Debug mode, appends to logfile.\n");
  256.     fprintf(errfile, "\t\t[-L file]   Log file for debugging info and errors.\n");
  257.     fprintf(errfile, "\t\t             (Default log file is stderr.)\n");
  258.     fprintf(errfile, "\t\t[-R]        Reset parallel port before sending data.\n");
  259.     fprintf(errfile, "\t\t[-K]        Ignore Interrupts.\n");
  260.     fprintf(errfile, "\t\t[-I]        Enable sensing of EOI pin, normally ignored.\n");
  261.     fprintf(errfile, "\t\t[-B int]    Internal buffer size in bytes.\n");
  262.     fprintf(errfile, "\t\t[-s]        Status-only invocation.\n");
  263.     fprintf(errfile, "\t\t             Checks the parallel port and updates\n");
  264.     fprintf(errfile, "\t\t             paper size (if specified)\n");
  265.     fprintf(errfile, "\t\t[-u]        Do not check the parallel port status.\n");
  266.     fprintf(errfile, "\t\t             Ignored if -s was NOT specified\n");
  267.     fprintf(errfile, "\t\t             Allows driver to update pod status\n");
  268.     fprintf(errfile, "\t\t             while ignoring parallel port\n");
  269.     fprintf(errfile, "\t\t[-S string] Paper Size (e.g., A, A4, Legal).\n");
  270.     fprintf(errfile, "\t\t             If specified, the pod status file for\n");
  271.     fprintf(errfile, "\t\t             the printer is updated.\n");
  272.     fprintf(errfile, "\t\t[-d]        Send a CTRL-D (ASCII 004) at end of job.\n");
  273.     fprintf(errfile, "\t\t[filename]  File to send, assumed <stdin> if none given.\n");
  274.  
  275.     fprintf(errfile, "\n");
  276.  
  277. }
  278.  
  279. /***********************************************************************/
  280.  
  281. void process_args(int argc, char* argv[])
  282.     /*
  283.      * REQUIRES: argc, argv, optind in valid format
  284.      * MODIFIES: has side effects on global state, like DEBUG, files, etc..
  285.      *
  286.      * EFFECTS:  Processes all the command line args using getopts.
  287.      *           Sets a bunch of state depending on which args are passed.
  288.      */
  289. {
  290.   int c;
  291.  
  292.   while ((c = getopt(argc, argv, "KdeuswDP:RIL:B:O:C:S:fp:r:z:mq:n:tc:i:o:")) != -1) {
  293.       switch (c) {
  294.       case 'K':
  295.           ignore_interrupts = TRUE;
  296.           break;
  297.       case 'd':  /* Whether we exit immediately on error: default is to retry */
  298.           send_ctrl_d = TRUE;
  299.           break;
  300.       case 'e':  /* Whether we exit immediately on error: default is to retry */
  301.           exit_on_error = TRUE;
  302.           break;
  303.       case 's':  /* Should we exit immediately after updating status? */
  304.           status_only = TRUE;
  305.           break;
  306.       case 'u':  /* Should we ignore plp port -- only use if -s specified? */
  307.           ignore_plp = TRUE;
  308.           break;
  309.       case 'S':
  310.           /* Attempt to convert this string to a page size. */
  311.           if ((user_page_size = PDGetSizeCodeByName(optarg)) < 0) {
  312.  
  313.                 /* If a PD error, then print that error code out */
  314.                 if (PDerrno != 0) {
  315.                     PDPerror(progname);
  316.                 } else {
  317.                     fprintf(stderr, "%s: Invalid page size '%s' selected.\n",
  318.                             progname, optarg);
  319.                 }
  320.                 /* We failed.   Fall back to our default page size. */
  321.                 fprintf(stderr, "%s: Using default page size '%s'.\n",
  322.                         progname, PDGetNameBySizeCode(DEFAULT_PAGE_SIZE));
  323.                 user_page_size = DEFAULT_PAGE_SIZE;
  324.           }
  325.           page_size_set = TRUE;
  326.           break;
  327.       case 'w':  /* Should we print warnings/info messages?  Default is yes */
  328.           print_warnings = FALSE;
  329.           break;
  330.       case 'D':  /* Turn on debugging logging */
  331.           DEBUG++;  /* Multiple -D flags increment level of detail. */
  332.           /* Coarse, medium, and fine detail are invoked by
  333.            * one, two, and three or more -D flags respectively.
  334.            */
  335.           switch (DEBUG) {
  336.               /* Print a message to logfile if debug > 0 */
  337.           case 0:
  338.               break;
  339.           case COARSEDETAIL:
  340.               fprintf(errfile,"%s: Coarse detail debug messages enabled.\n",
  341.                       progname);
  342.               break;
  343.           case MEDIUMDETAIL:
  344.               fprintf(errfile,"%s: Medium detail debug messages enabled.\n",
  345.                       progname);
  346.               break;
  347.           case FINEDETAIL:
  348.           default:
  349.               fprintf(errfile, "%s: Fine detail debug messages enabled.\n",
  350.                       progname);
  351.               break;
  352.           }
  353.           break;
  354.       case 'P':  /* REQUIRED: name of printer as installed under SysV spooler */
  355.           printername = strdup(optarg);
  356.           break;
  357.       case 'R':  /* Whether we should reset the parallel port.
  358.                   * This is not a required option, just one added for this driver
  359.                   */
  360.           reset_port = TRUE;
  361.           break;
  362.       case 'I':  /* Whether we should ignore EOI, default is TRUE.*/
  363.           ignore_EOI = FALSE;
  364.           break;
  365.       case 'L':  /* Where to write logging information.  Default is stderr */
  366.           if (!(errfile = fopen(optarg,"a"))) {
  367.               errfile = stderr;
  368.               fprintf(errfile,"%s:\tUnable to open logfile %s for writing.\n",
  369.                       progname, optarg);
  370.               fprintf(errfile,"%s:\tUsing standard error instead.\n",
  371.                       progname);
  372.           }
  373.           break;
  374.       case 'B':  /* How big a buffer to allocate for data */
  375.           if (atoi(optarg) <= 0)
  376.               fprintf(errfile,
  377.                       "%s: -%c (buffer size) option must have a positive argument.\n",
  378.                       progname, (char)c);
  379.           else inbufsize = atoi(optarg);
  380.           break;
  381.  
  382.           /* The following options are those which this driver either does not
  383.            * or cannot implement, due to limitations on its intelligence
  384.            * about the input format and printer format.
  385.            *
  386.            * They are reproduced here for your convenience, so that
  387.            * when you the developer implement the full set you have a
  388.            * checklist here for your use.
  389.            */
  390.       case 'O':  /* Where to write our output data. */
  391.       case 'f':  /* flip input image */
  392.       case 'p':  /* scale to match a given pixels-per-inch input resolution */
  393.       case 'r':  /* rotate the image, an integer number of degrees */
  394.       case 'z':  /* zoom the image, a floating point argument */
  395.       case 'm':  /* whether to request manual feed */
  396.       case 'q':  /* quality mode, an integer */
  397.       case 'n':  /* number of copies, an integer, model file handles this */
  398.       case 't':  /* print a test page */
  399.       case 'o':  /* output media type, for color matching */
  400.           fprintf(errfile,
  401.                   "%s: -%c option not handled by this driver, ignoring it.\n",
  402.                   progname, (char)c);
  403.           break;
  404.       case '?': /* getopts didn't recognize this option from list above */
  405.           print_usage_message();
  406.           exit(2);
  407.       }
  408.   }
  409.  
  410.   /* ignore_plp can only be true when status_only is true */
  411.  
  412.   if (!status_only) ignore_plp = FALSE;
  413.  
  414.   /* was a printer name specified?  If not, exit */
  415.  
  416.   if (printername == NULL) {
  417.       fprintf(errfile, "%s: Printer name argument required:\n", progname);
  418.       fprintf(errfile, "%s: \tYou must use the -P option to specify the "
  419.               "printer name.\n", progname);
  420.       exit(2);
  421.   }
  422.  
  423.   /* Was a filename specified after the last argument?
  424.    * If so, grab it and try to open it instead of stdin.
  425.    */
  426.  
  427.   if (optind < argc) {
  428.       fprintf(errfile, "%s: reading from %s\n", progname, argv[argc-1]);
  429.       infile = fopen(argv[argc-1], "r");
  430.       if (!infile) {
  431.           /* In future versions, we will use libpod to write errors to the
  432.            * printer's .log file.  See libpod man page for PDWriteLog.
  433.            */
  434.           fprintf(errfile, "%s: Could not open input file %s for reading\n",
  435.                   progname, argv[argc-1]);
  436.           exit(1);
  437.       }
  438.   }
  439.  
  440.   return;
  441. }
  442.  
  443.  
  444. /***********************************************************************/
  445.  
  446. int printer_error(int error_code)
  447.     /*
  448.      *  EFFECTS:
  449.      *          Appends one error to the global errorMessages array
  450.      *          and increments global variable pod->error_count by one.
  451.      *
  452.      *          This function adds an error code and corresponding message
  453.      *          to the active error status.  A message that matches the
  454.      *          error code is added to the structure along with the error
  455.      *          code.
  456.      *
  457.      *          If the number of messages would be increased over PD_MESSAGE_MAX,
  458.      *          then no message is added to the error_status struct.
  459.      *
  460.      *  RETURNS:
  461.      *          Returns the lesser of the error count and PD_MESSAGE_MAX.
  462.      *
  463.      *  ASSUMPTIONS:
  464.      *          The error status will be reset by setting error_count to zero
  465.      *          before a new set of error status is reported.
  466.      *          Set "pod->active_status->error_count = 0;"
  467.      *
  468.      *          Assumes the following global variables:
  469.      *          static  PDInfoStruct  *pod;
  470.      *          static  PDMessageStruct errorMessages[PD_MESSAGE_MAX];
  471.      *
  472.      */
  473. {
  474.     static char msgText[PD_STR_MAX]; /* enough memory for longest legal message */
  475.  
  476.     if (pod->active_status->error_count >= PD_MESSAGE_MAX) {
  477.         return PD_MESSAGE_MAX;
  478.     }
  479.  
  480.     switch (error_code) {
  481.  
  482.         /* First we handle any special cases we want to report that
  483.          * the libpod library may not take care of for us in the fashion we desire.
  484.          * We'll demonstrate the safer, easier default method below.
  485.          */
  486.  
  487.     case PD_MSG_MASK_VERSION:
  488.         /*
  489.          * Case in point:  The version message is not handled in libpod,
  490.          *  because it requires application-specific information.
  491.          *  In this case we'll make our own message.
  492.          */
  493.         (void) sprintf(msgText, "%s", PhandlerDriverVersion);
  494.  
  495.         /* We've got our string.  Now let's put it into errorMessages.
  496.          * Do this very safely so developers can change the above at will.
  497.          */
  498.         errorMessages[pod->active_status->error_count].message_code = error_code;
  499.  
  500.         /* Copy only as many characters as are guaranteed to fit, less the null */
  501.  
  502.         (void) strncpy(errorMessages[pod->active_status->error_count].message_text,
  503.                        msgText, PD_STR_MAX - 1 );
  504.  
  505.         /* Now make sure to put a null at the end of the string. */
  506.  
  507.         errorMessages[pod->active_status->error_count].message_text[PD_STR_MAX-1]
  508.             = '\0';
  509.  
  510.         break;
  511.  
  512.     default:
  513.         /*
  514.          * Now that we've handled any special cases, pass the rest off to the 
  515.          * default handler supplied by libpod.  This is the easiest and safest 
  516.          * method, but we had to demonstrate both, now didn't we?
  517.          */
  518.         PDMakeMessage(&errorMessages[pod->active_status->error_count], error_code);
  519.  
  520.         break;
  521.     }
  522.  
  523.     /* NOTE: *DO NOT INCREMENT ERROR_COUNT IN THE CODE ABOVE*
  524.      * unless you absolutely MUST add multiple messages at once,
  525.      * because pod->error_count will be incremented by one on exit.
  526.      *
  527.      * Instead of adding multiple messages at once, you should just call 
  528.      * printer_error multiple times, once with each message you wish.
  529.      */
  530.     return ++pod->active_status->error_count;
  531. }
  532.  
  533. /***********************************************************************/
  534.  
  535. int update_status(int cur_status) {
  536.     /* REQUIRES: parallel port open for reading
  537.      *           cur_status is what the printer status SHOULD be,
  538.      *           barring other errors....
  539.      * MODIFIES: active status file, errfile
  540.      * EFFECTS:  reads parallel port status and reports it into
  541.      *            printer active status file.
  542.      * ASSUMES:  We will not report more than PD_ERROR_MAX errors.
  543.      * RETURNS:  TRUE if reading status was successful, else FALSE.
  544.      */
  545.  
  546.     int    iostat;                 /* the I/O port status byte, per plp.h */
  547.     pid_t  parent_pid;
  548.  
  549.     /* IMPORTANT: Caller should hold signals when called. */
  550.  
  551.     /* Set the status to be the cur_status. If there are more pressing issues 
  552.      * to report, the default status indicated in cur_status will be 
  553.      * overridden below.
  554.      */
  555.     pod->active_status->operational_status = cur_status;
  556.  
  557.     if (!ignore_plp) {
  558.  
  559.         /* Read the status byte from the parallel port */
  560.  
  561.         while ((iostat = ioctl(fileno(stdout), PLPIOCSTATUS, 0)) == -1) {
  562.  
  563.              fprintf(errfile,
  564.                 "%s: Could not read status from parallel port (filedes #%d.)",
  565.                 progname, fileno(stdout));
  566.  
  567.              if (exit_on_error) {
  568.  
  569.                  fprintf(errfile, "  Exiting after iostat error on "
  570.                          " parallel port\n");
  571.                  return TRUE;
  572.  
  573.              } else {
  574.  
  575.                  fprintf(errfile,"  Retrying in %d seconds....\n", 
  576.                          pod->error_retry_wait);
  577.                  (void) sleep(pod->error_retry_wait);
  578.             }
  579.         }
  580.  
  581.         /* Interpret that byte:  */
  582.  
  583.     /* Decide whether to ignore the PLPEOI bit, depending on cmd
  584.      * line switches.  It is almost always high because few printer
  585.      * manufacturers obey the centronics protocol to report this
  586.      * bit, and they generally simply wire this pin to chassis
  587.      * ground.
  588.          */
  589.  
  590.         if (ignore_EOI) {
  591.             iostat &= ~PLPEOI;  /* mask out the EOI pin, it's often invalid */
  592.          }
  593.  
  594.         if (DEBUG) {
  595.             fprintf(errfile, "%s: Status = %d\n", progname, iostat);
  596.         }
  597.  
  598.         /* Set up to write the active status file error messages.
  599.          * Reset the error_count to start over with new messages.
  600.          */
  601.         pod->active_status->error_count = 0;
  602.  
  603.         switch(iostat) {
  604.         case (PLPONLINE):
  605.  
  606.         /* Printer is online, all status OK.  Make
  607.          * operational_status the state indicated by the passed in
  608.          * argument cur_status, which varies depending on where in
  609.          * the program we are.
  610.              */
  611.  
  612.         pod->active_status->operational_status = cur_status; 
  613.             break;
  614.  
  615.         case ( 0 ):
  616.             /* Printer is offline, all other status OK */
  617.             (void) printer_error(PD_ERROR_OFFLINE);
  618.             pod->active_status->operational_status = PD_STATUS_FAULTED;
  619.             break;
  620.  
  621.         case (PLPEOP):
  622.             /* Printer is offline and out of paper */
  623.             (void) printer_error(PD_ERROR_PAPER_OUT);
  624.             (void) printer_error(PD_ERROR_OFFLINE);
  625.             pod->active_status->operational_status = PD_STATUS_FAULTED;
  626.             break;
  627.  
  628.         case (PLPFAULT):
  629.             /* Printer is faulted, no specific reason given. */
  630.             (void) printer_error(PD_ERROR_OFFLINE);
  631.             (void) printer_error(PD_ERROR_OTHER_FAULT);
  632.             pod->active_status->operational_status = PD_STATUS_FAULTED;
  633.             break;
  634.  
  635.         case (PLPFAULT + PLPEOP):
  636.             /* Printer is faulted and out of paper */
  637.             (void) printer_error(PD_ERROR_PAPER_OUT);
  638.             (void) printer_error(PD_ERROR_OTHER_FAULT);
  639.             pod->active_status->operational_status = PD_STATUS_FAULTED;
  640.             break;
  641.  
  642.         case (PLPEOI):
  643.         case (PLPFAULT + PLPEOI):
  644.             (void) printer_error(PD_ERROR_INK_OUT);
  645.             pod->active_status->operational_status = PD_STATUS_FAULTED;
  646.             break;
  647.  
  648.         case (PLPONLINE + PLPEOP):
  649.         case (PLPONLINE + PLPEOI):
  650.         case (PLPFAULT  + PLPONLINE + PLPEOP):
  651.         case (PLPFAULT  + PLPONLINE):
  652.             /*
  653.             * All these cases are invalid combinations.
  654.             * We assume that these mean there is no printer connected.
  655.             *
  656.             * These bits may go high when you pull the cable loose,
  657.             * or turn off the printer.
  658.             */
  659.             (void) printer_error(PD_ERROR_NOT_RESPONDING);
  660.             pod->active_status->operational_status = PD_STATUS_UNAVAILABLE;
  661.             break;
  662.  
  663.         default:
  664.  
  665.         /* unknown combination .... report "other fault".  We
  666.          * shouldn't reach this statement, but good programming
  667.          * convention demands that always include a default case in
  668.          * every switch statement!
  669.              */
  670.  
  671.         (void) printer_error(PD_ERROR_OTHER_FAULT);
  672.         pod->active_status->operational_status = PD_STATUS_UNAVAILABLE; 
  673.             break;
  674.  
  675.        }
  676.  
  677.     } /* if (!ignore_plp) */
  678.  
  679.     /* If warnings info is allowed, add the optional driver version now.  */
  680.  
  681.     if (print_warnings) {
  682.         (void) printer_error(PD_MSG_MASK_VERSION);
  683.     }
  684.  
  685.     /* Set the page size to whatever the user has requested. */
  686.  
  687.     if (page_size_set) {
  688.         pod->active_status->media_size = user_page_size;
  689.     }
  690.  
  691.     /* Caller should hold signals so we won't interrupt any changes to
  692.      * the pod database in mid-change.
  693.      */
  694.  
  695.     if (DEBUG) {
  696.         fprintf(stderr,"%s: Updating printer POD status file.\n", progname);
  697.     }
  698.  
  699.     PDLocalWriteStatus(printername, pod->active_status, errorMessages);
  700.  
  701.     return TRUE;
  702. }
  703.  
  704. /***********************************************************************/
  705.  
  706. void reset_plp(void) 
  707. {
  708.  
  709.     if (DEBUG) {
  710.           fprintf(errfile, "%s: Resetting parallel port...\n", progname);
  711.     }
  712.  
  713.     while (ioctl(fileno(stdout), PLPIOCRESET, 0) == -1) {
  714.  
  715.         fprintf(errfile,"%s: Could not reset the parallel port.", progname);
  716.  
  717.         if (exit_on_error) {
  718.             fprintf(errfile,"Exiting.\n");
  719.             exit(1);
  720.         } else {
  721.             /* Retry indefinitely until successful or KILLed. */
  722.             fprintf(errfile,"Retrying in %d seconds....\n",
  723.                       pod->error_retry_wait);
  724.             (void) sleep(pod->error_retry_wait);
  725.         }
  726.     }
  727. }
  728.  
  729. /***********************************************************************/
  730.  
  731. void open_plp(void) 
  732. {
  733.  
  734.   /* Open port.
  735.    *
  736.    * Exit only if error exit is requested on command line.
  737.    * If open fails, issue one message and wait indefinitely.
  738.    *
  739.    * IMPORTANT PHILOSOPHICAL NOTE:
  740.    *
  741.    * If exit_on_error has not been set, DO NOT EXIT *EVER* unless killed.
  742.    * This ensures that no job falls off the end of the queue without some
  743.    * sort of printed output, or an explicit job cancellation.
  744.    *
  745.    * This philosophy is consistent throughout, so adhere to it!
  746.    */
  747.  
  748.   if (DEBUG > MEDIUMDETAIL) {
  749.     fprintf(errfile, "%s: Opening parallel port '%s'.\n",
  750.             progname, pod->port_path);
  751.   }
  752.  
  753.   /* Compare only first 8 charaters to make sure it is a parallel port.
  754.    * Valid ports are /dev/plp[n].  Larger systems can have multiple
  755.    * parallel ports.  See plp(7) man page.
  756.    */
  757.  
  758.   if (strncmp(pod->port_path, "/dev/plp",8)) {
  759.       fprintf(errfile, "%s: Device '%s' specified in config file is not "
  760.              "supported: Exiting.\n", progname, pod->port_path);
  761.       fprintf(errfile, "%s: Please modify config file to list /dev/plp as "
  762.              "the printer connection device.", progname);
  763.       exit(1);
  764.   }
  765.  
  766.   while (freopen(pod->port_path, "w", stdout) == NULL) {
  767.  
  768.       /* If we got here, we have an error while opening the port */
  769.  
  770.       if (exit_on_error) {  /* Exit immediately. */
  771.  
  772.           /* Print a message in the common case where the port was busy. */
  773.  
  774.           if (errno == EBUSY) {
  775.               fprintf(errfile,
  776.                       "%s: Output port %s busy -- cannot open.  Exiting...\n",
  777.                       progname, pod->port_path);
  778.               exit(1);
  779.           } else {
  780.  
  781.               fprintf(errfile,
  782.                       "%s: Couldn't open output port %s, error #%d. Exiting.\n",                      progname, pod->port_path, errno);
  783.               exit(1);
  784.           }
  785.  
  786.       } else {
  787.  
  788.           /*
  789.            * Exit_on_error not set:
  790.            *  Loop indefinitely trying to open the port,
  791.            *  checking every pod->error_retry_wait seconds.
  792.            */
  793.           fprintf(errfile, "%s: Output port %s could not be opened "
  794.                   "(error #%d). Retrying in %d seconds...\n",
  795.                   progname, pod->port_path, errno, pod->error_retry_wait);
  796.           (void) sleep(pod->error_retry_wait);
  797.       }
  798.   }
  799.  
  800.   /* ASSERTION: If we got here, we successfully opened the parallel port. */
  801.  
  802.   if (DEBUG > MEDIUMDETAIL) {
  803.       fprintf(errfile, "%s: Successfully opened parallel port.  Now trying "
  804.              "to lock port....\n", progname);
  805.   }
  806.  
  807.   /* Put an advisory file lock on the port. First we try a
  808.    * nonblocking lock so we can report whether our first try
  809.    * succeeded. If it failed because it would have blocked, report
  810.    * that and try a blocking lock.  We report so that a blocked lock
  811.    * can be diagnosed by the user.
  812.    */
  813.  
  814.   if (flock(fileno(stdout), LOCK_EX | LOCK_NB) < 0) {
  815.  
  816.       fprintf(stderr,"%s: Error #%d while locking parallel port.\n",
  817.               progname, errno);
  818.  
  819.       if (errno != EWOULDBLOCK) {
  820.           perror(progname);
  821.           exit(1);
  822.       } else {
  823.          /* errno == EWOULDBLOCK: someone else has it locked already.
  824.           * report that someone has it locked, then do a blocking lock.
  825.           */
  826.          /* In future versions, we will use libpod to write to .log file.
  827.           * See libpod man page, specifically for PDWriteLog.
  828.           */
  829.  
  830.          fprintf(errfile, "%s: Output port locked by another process.  "
  831.                  "Waiting for lock.\n", progname);
  832.  
  833.          if (flock(fileno(stdout), LOCK_EX) < 0) {
  834.             perror(progname);
  835.             exit(1);
  836.          }
  837.       }
  838.   }
  839.  
  840.   /* ASSERTION: if we got here, our lock succeeded.  We now own the port. */
  841.  
  842.   if (DEBUG > MEDIUMDETAIL) {
  843.       fprintf(errfile, "%s: Successfully locked parallel port.\n", progname);
  844.   }
  845.  
  846. }
  847.  
  848.  
  849. /***********************************************************************/
  850.  
  851. main(int argc, char** argv)
  852. {
  853.   short empty_infile  = TRUE;
  854.  
  855.   /* set the progname variable to the stripped program name */
  856.  
  857.   set_program_name( argv[0] );
  858.  
  859.   /* Catch the killing signals so that proper cleanup can be done
  860.    * This is important in the UNIX environment because many
  861.    * software interrupts are possible.  We catch the most common
  862.    * catchable interrupts.  Cleanup_and_quit is a void(int ...) func which
  863.    * should clean up the printer, and then exit the program.
  864.    */
  865.  
  866.   (void) setup_signal_handler();
  867.  
  868.   /* Parse command line options */
  869.  
  870.   process_args( argc, argv );
  871.  
  872.   /* Open the POD database files. */
  873.  
  874.   if (PDLocalReadInfo(printername, &pod, &podmodtime) < 0)  {
  875.       fprintf(errfile, "%s: Could not open required POD database files "
  876.               "for printer %s.\n", progname, printername);
  877.       fprintf(errfile, "%s: Are you sure all required POD files are "
  878.               "properly installed?\n", progname);
  879.       PDPerror(progname);
  880.       exit(2);
  881.   }
  882.  
  883.   if (DEBUG > MEDIUMDETAIL) {
  884.     fprintf(errfile, "%s: Successfully read printer POD files.\n", progname);
  885.   }
  886.  
  887.   if (!ignore_plp) {
  888.       (void) open_plp();
  889.   }
  890.  
  891.   /* Update status.  If status-only is true, exit after updating status. */
  892.  
  893.   hold_signals();
  894.  
  895.   if (status_only == TRUE) {
  896.     /* Then update the status with idle default
  897.      * (since we're exiting immediately) and then exit. */
  898.     exit(update_status(PD_STATUS_IDLE) == FALSE);
  899.   } else {
  900.     (void) update_status(PD_STATUS_BUSY);
  901.   }
  902.  
  903.   /* Now reset the parallel port if that was requested via cmd line option. */
  904.  
  905.   if (!ignore_plp && reset_port) {
  906.       (void) reset_plp();
  907.   }
  908.  
  909.   /* Allow signals to interrupt  from here on.  See cleanup_and_quit function
  910.    * to see how signals are hndled.
  911.    */
  912.  
  913.   release_signals(); 
  914.  
  915.   /* Allocate and set up the input buffer */
  916.  
  917.   if (inbufsize < 128) inbufsize = BUFDEFSIZE;
  918.   inbuf = (char *)malloc(inbufsize);
  919.   if (inbuf == NULL) {
  920.       fprintf(errfile,"%s: Could not allocate requested buffer of %d bytes.\n",
  921.               progname, inbufsize);
  922.       exit(1);
  923.   }
  924.  
  925.   if (DEBUG > COARSEDETAIL) {
  926.       fprintf(errfile,"%s: Allocated buffer of %d bytes.\n",progname,inbufsize);
  927.   }
  928.  
  929.   /* Now read status, update status db, and send data.  We don't want
  930.    * to block status updating waiting for i/o.  We also want to keep
  931.    * the internal buffer (if any) as full as possible while sending
  932.    * data.  If infile is empty, exit with an error code.
  933.    */
  934.  
  935.   /* To avoid blocking, let's fork and have a child do the status
  936.    * updating while the main process does its reading and writing.
  937.    */
  938.  
  939.   (void) signal(SIGCLD, SIG_IGN); /* ignore child death, don't make zombies */
  940.  
  941.   if ( (child_pid = fork()) == (pid_t) -1 ) {
  942.       fprintf(stderr, "%s: can't fork child process to update status, "
  943.               "status may be inaccurate", progname);
  944.   }
  945.  
  946.   /*
  947.    * NOTE TO DEVELOPER:
  948.    * If you don't understand the implications of forking multiple processes,
  949.    * please read at least the fork() man page.
  950.    */
  951.  
  952.   if (child_pid == (pid_t) 0) { /* If pid == 0, we're the child process. */
  953.  
  954.       /* Register ourselves to get a SIGHUP when the parent process dies. */
  955.  
  956.       (void)prctl(PR_TERMCHILD);
  957.  
  958.       if (DEBUG) {
  959.           fprintf(stderr, "%s: Forked a child to read status.\n", progname);
  960.       }
  961.  
  962.       /* Now just keep updating status, sleeping between polls. */
  963.  
  964.       while (1) {
  965.           hold_signals();
  966.           (void) update_status(PD_STATUS_BUSY);
  967.           release_signals();
  968.           (void) sleep(pod->error_retry_wait);
  969.       }
  970.       /* NOTREACHED IF CHILD */
  971.   }
  972.  
  973.   /* We are the parent process, do the rest of the normal thing you'd do. 
  974.    *
  975.    * NOTE: SIgnals are off in this loop -- it can be interrupted 
  976.    */
  977.  
  978.   while(!feof(infile)) {
  979.  
  980.       /* read and update the status if no child watching status */
  981.  
  982.       if (child_pid == (pid_t) -1) {
  983.               hold_signals();
  984.               (void) update_status(PD_STATUS_BUSY);
  985.               release_signals();
  986.       }
  987.  
  988.       inbytesleft = fread(inbuf, 1, inbufsize, infile);
  989.  
  990.       if (inbytesleft > 0) empty_infile = FALSE;
  991.  
  992.       if (ferror(infile)) {
  993.  
  994.           fprintf(errfile, "%s: Error #%d reading input pipe, exiting.\n",
  995.                   progname, errno);
  996.  
  997.           if (child_pid != (pid_t) -1) {
  998.               (void) kill(child_pid, SIGHUP);
  999.               (void) sleep(1);
  1000.           }
  1001.  
  1002.           hold_signals();
  1003.           (void) update_status(PD_STATUS_FAULTED);
  1004.  
  1005.           exit(errno);
  1006.       }
  1007.  
  1008.       /*
  1009.        * NOTE TO DEVELOPERS:
  1010.        *
  1011.        * Insert subroutine here to process the input data
  1012.        * from intermediate format to printer format.
  1013.        *
  1014.        * phandler does not do this since it is not a printer-specific
  1015.        * driver, and is only an example.  Your driver will know
  1016.        * both what kind of printer it is driving
  1017.        * and what kind of data it is receiving.
  1018.        *
  1019.        * Therefore you will want to do data-specific processing here,
  1020.        * which will in turn improve your error-recovery ability.
  1021.        */
  1022.  
  1023.       (void) fwrite(inbuf,1,inbytesleft,stdout);
  1024.  
  1025.       if (ferror(stdout)) {
  1026.  
  1027.           int exitcode = errno;
  1028.           fprintf(errfile, "%s: Error #%d writing output pipe.\n",
  1029.                   progname, exitcode);
  1030.  
  1031.           if (child_pid != (pid_t) -1) {
  1032.               (void) kill(child_pid, SIGHUP);
  1033.               (void) sleep(1);
  1034.           }
  1035.  
  1036.           hold_signals();
  1037.           (void) update_status(PD_STATUS_FAULTED);
  1038.  
  1039.           exit(exitcode);
  1040.       }
  1041.  
  1042.       (void) fflush(stdout);
  1043.  
  1044.   } /* end while */
  1045.  
  1046.   if (inbuf) {
  1047.       if (send_ctrl_d) {
  1048.           inbuf[0]='\004';
  1049.           inbuf[1]='\0';
  1050.           (void) fwrite(inbuf,1,1,stdout);
  1051.       }
  1052.   }
  1053.  
  1054.   /* Reset printer and exit, updating status on the way. */
  1055.  
  1056.   hold_signals();
  1057.  
  1058.   if (child_pid != (pid_t) -1) {
  1059.       (void) kill(child_pid, SIGHUP);
  1060.       (void) sleep(1);  /* wait a while for child to go away */
  1061.   }
  1062.  
  1063.   /* We're done, reset state to idle */
  1064.  
  1065.   (void) update_status(PD_STATUS_IDLE);
  1066.  
  1067.   /* Now obey the convention that we exit error code 1 if no data was read.
  1068.    * This ensures that shell scrips which call us as the last stage in a
  1069.    * pipe can catch error exits of the filters upstream of us.
  1070.    */
  1071.  
  1072.   if (empty_infile == TRUE) exit(1);
  1073.  
  1074.   return 0;
  1075.  
  1076. }
  1077.  
  1078.  
  1079.  
  1080.  
  1081.  
  1082.  
  1083.  
  1084.  
  1085.  
  1086.